# -*- coding:utf8 -*-
import json
import traceback
import os
import typing
import ddddocr
import binascii
from Crypto.Cipher import AES, DES
from Crypto.Util.Padding import unpad, pad
import base64
from scrapy import Request, FormRequest
from squirrel_core.commons.utils.tools import calc_str_md5, upload_file
from squirrel_core.frame.spider_makaka import MakakaSpider

file_path = os.environ.get("FILE_PATH", "/")


class wenguang_score(MakakaSpider):
    name = "wenguang_score"
    serialNumber = ""
    des_key = ""
    app_id = ""
    header = {
        "Host": "xm.xcmsports.com",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf2541022) XWEB/16467",
        "Accept": "application/json",
        "Content-Type": "application/json;charset=utf-8"
    }
    moran_header = {
        "Host": "moran.zjim.org",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf2541022) XWEB/16467",
        "xweb_xhr": "1",
        "Content-Type": "application/json",
        "Accept": "*/*"
    }
    match_url = "https://moran.zjim.org/api/routepost/scorequery/match"
    captcha_url = "https://xm.xcmsports.com/api/common-service/sendsmsrecord/common/captcha"
    score_url = "https://xm.xcmsports.com/api/charitable-service/marathonachievement/qryMyMarathonAchievementInfo"
    cert_url = "https://xm.xcmsports.com/api/charitable-service/marathonachievement/createMarathonAchievementFileByPersonSubscribeId"

    def get_ext_requests_or_urls(self, data=None):
        if data and isinstance(data, str):
            data = json.loads(data)
        if data and isinstance(data, typing.MutableMapping):
            self.serialNumber = data.get("serialNumber", "")
            race_id = data.get("spider_config", {}).get("race_id", "")
            user_id = data.get("spider_config", {}).get("user_id", "")
            race_no = data.get("spider_config", {}).get("race_no", "")
            user_name = data.get("spider_config", {}).get("user_name", "")
            card_num = data.get("spider_config", {}).get("card_num", "")
            race_name = data.get("spider_config", {}).get("race_name", "").replace(" ", "")
            home_url = data.get("spider_config", {}).get("url", "")
            if not card_num:
                self.logger.info("card_num必须存在")
                self.upload_procedure({"serialNumber": self.serialNumber, "code": 203, "message": "card_num必须存在"})
                self.close_after_idle = True
                self.force_to_close_spider = True
            else:
                self.upload_procedure({"serialNumber": self.serialNumber, "code": 100, "message": "任务启动成功"})
                if "#小程序://蓦然体育" in home_url:
                    if "2025金华山问鼎1314速攀赛" in race_name:
                        self.app_id = "wx58cb6503a557b24a"
                        self.des_key = f"ts.appid.{self.app_id}"
                        data = {"appid":self.app_id,"systemid":"12822283","userid":726406922,
                                "ver":"3.9.11.7","env":"release","channel":"","paramobjid":717074482,"paramval1":card_num}
                        encrypt_data = self.encryptDes(json.dumps(data))
                        post_data = {"appid": self.app_id, "text": encrypt_data}
                        yield Request(url=self.match_url, callback=self.parse_moran, dont_filter=True, errback=self.err_parse,
                                      method="POST", body=json.dumps(post_data), headers=self.moran_header,
                                      meta={"race_id": race_id, "user_id": user_id, "user_name": user_name,
                                            "race_name": race_name, "card_num": card_num, "race_no": race_no, "p_data": data})
                else:
                    yield Request(url=self.captcha_url, callback=self.parse, dont_filter=True, errback=self.err_parse, headers=self.header,
                                  meta={"race_id": race_id, "user_id": user_id, "user_name": user_name,
                                        "race_name": race_name, "card_num": card_num, "race_no": race_no})

    def parse(self, response, **kwargs):
        race_id = response.meta.get("race_id", "")
        user_id = response.meta.get("user_id", "")
        race_name = response.meta.get("race_name", "")
        user_name = response.meta.get("user_name", "")
        race_no = response.meta.get("race_no", "")
        card_num = response.meta.get("card_num", "")
        try:
            self.logger.info(f"{user_name}开始打码")
            content = json.loads(response.text)
            key = content.get("key", "")
            image = content.get("image", "")
            ocr = ddddocr.DdddOcr(beta=True)
            yzm = ocr.classification(image.replace("data:image/png;base64,", ""))
            post_data = {"certNo": card_num, "captchaValue": yzm, "captchaKey": key}
            self.header["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"
            yield FormRequest(self.score_url, callback=self.parse_score, errback=self.err_parse,
                              formdata=post_data, headers=self.header, dont_filter=True,
                              meta={"race_id": race_id, "race_no": race_no, "user_id": user_id,
                                    "user_name": user_name, "race_name": race_name, 'handle_httpstatus_list': [400, 200]})
        except Exception:
            self.logger.info(f"{user_name}打码错误：{traceback.format_exc()}")

    def parse_score(self, response):
        race_id = response.meta.get("race_id", "")
        user_id = response.meta.get("user_id", "")
        race_name = response.meta.get("race_name", "")
        user_name = response.meta.get("user_name", "")
        race_no = response.meta.get("race_no", "")
        try:
            self.logger.info(f"获取{user_name}的成绩信息")
            data_list = json.loads(response.text).get("data", {}).get("marathonAchievementList", [])
            score_data = {}
            for data in data_list:
                r_name = data.get("eventPlayName", "")
                project_name = data.get("projectName", "")
                r_no = data.get("playerNo", "")
                if race_name in r_name and r_no == race_no:
                    cert_id = data.get("id", "")
                    score_data = {"race_name": race_name, "itemName": project_name,
                                  "name": user_name, "raceNo": race_no,
                                  "pace": data.get("avgPace", ""), "speed": data.get("avgSpeed", ""),
                                  "distance": "", "shotScore": data.get("gunTime210975", ""),
                                  "score": data.get("netTime210975", "")}
                    break
            if score_data:
                yield FormRequest(self.cert_url, callback=self.parse_cert, errback=self.err_parse,
                                  formdata={"achievementId": cert_id}, headers=self.header, dont_filter=True,
                                  meta={"race_id": race_id, "race_no": race_no, "user_id": user_id,
                                        "score_data": score_data, "user_name": user_name, "race_name": race_name})
            else:
                result = self.result_item_assembler(response)
                result['result_data'] = {"serialNumber": self.serialNumber, "webType": self.name_first,
                                         "crawlerType": self.name_second, "data": str({"msg": "未查到相关成绩信息"})}
                yield result
        except Exception:
            self.logger.info(f"获取{user_name}的成绩信息错误：{traceback.format_exc()}")

    def parse_cert(self, response):
        race_id = response.meta.get("race_id", "")
        user_id = response.meta.get("user_id", "")
        user_name = response.meta.get("user_name", "")
        race_no = response.meta.get("race_no", "")
        score_data = response.meta.get("score_data", {})
        try:
            self.logger.info(f"获取{user_name}的证书信息")
            data = json.loads(response.text).get("data", "")
            content = json.loads(self.decrypt(data))
            file_name = content.get("fileName", "")
            file_link = content.get("fileLocationPath", "")
            if file_name:
                cert_url = f"https://xm.xcmsports.com/file/{file_link}/{file_name}"
                yield Request(url=cert_url, callback=self.parse_pic, errback=self.err_parse, dont_filter=True,
                              meta={"race_id": race_id, "user_name": user_name, "user_id": user_id,
                                    "score_data": score_data, "race_no": race_no})
            else:
                result = self.result_item_assembler(response)
                result['result_data'] = {"serialNumber": self.serialNumber, "webType": self.name_first,
                                         "crawlerType": self.name_second, "data": str({"msg": "未查到相关成绩信息"})}
                yield result
        except Exception:
            self.logger.info(f"获取{user_name}的证书信息错误：{traceback.format_exc()}")

    def parse_pic(self, response):
        race_id = response.meta.get("race_id", "")
        user_id = response.meta.get("user_id", "")
        user_name = response.meta.get("user_name", "")
        race_no = response.meta.get("race_no", "")
        score_data = response.meta.get("score_data", {})
        try:
            self.logger.info(f"开始下载{user_name}的证书：{response.url}")
            pic_md5 = calc_str_md5(response.url)
            pic_name = f"{race_no}_{pic_md5}.jpg"
            dir_path = os.path.join(file_path, "picture", self.name_first)
            if not os.path.exists(dir_path):
                os.makedirs(dir_path)
            save_path = os.path.join(dir_path, pic_name)
            if not os.path.exists(save_path):
                with open(save_path, "wb") as f:
                    f.write(response.body)
                upload_path = f"flow/{race_id}/{user_id}/pic/{pic_name}"
                upload_flag = upload_file(save_path, upload_path)
                if upload_flag:
                    self.logger.info(f"{save_path}上传成功：{upload_path}")
                else:
                    self.logger.info(f"{save_path}上传失败：{upload_path}")
                score_data["certImg"] = upload_path
                result = self.result_item_assembler(response)
                result['result_data'] = {"serialNumber": self.serialNumber, "webType": self.name_first,
                                         "crawlerType": self.name_second, "data": str(score_data)}
                yield result
        except Exception:
            self.logger.info(f"下载{user_name}证书时出错{response.url}：{traceback.format_exc()}")

    def parse_moran(self, response, **kwargs):
        race_id = response.meta.get("race_id", "")
        user_id = response.meta.get("user_id", "")
        race_name = response.meta.get("race_name", "")
        user_name = response.meta.get("user_name", "")
        race_no = response.meta.get("race_no", "")
        p_data = response.meta.get("p_data", "")
        try:
            self.logger.info(f"开始获取{user_name}的成绩信息")
            content = json.loads(response.text).get("text", "")
            content = json.loads(self.decryptDes(content))
            data_list = content.get("data", [])
            if data_list:
                score_data = ""
                for data in data_list:
                    data_dict = {}
                    d_list = data.get("scoredata", {}).get("showrow", [])
                    for d in d_list:
                        data_dict[d.get("title", "")] = d.get("val", "")
                    if data_dict.get("参赛号", "") == race_no:
                        score_data = {"race_name": race_name, "itemName": data_dict.get("项目", ""),
                                      "name": user_name, "raceNo": race_no,
                                      "pace": "", "speed": "",
                                      "distance": "", "shotScore": data_dict.get("枪声成绩", ""),
                                      "score": data_dict.get("净计时成绩", "")}
                    cert_data = data.get("scoredata", {}).get("certificates", [])
                    if cert_data:
                        cert_flag = cert_data[0].get("typeflag", "")
                        p_data.pop("paramobjid", "")
                        p_data.pop("paramval1", "")
                        p_data["typeflag"] = cert_flag
                        encrypt_data = self.encryptDes(json.dumps(p_data))
                        post_data = {"appid": self.app_id, "text": encrypt_data}
                        url = "https://moran.zjim.org/api/routepost/scorequery/certificatedown"
                        yield Request(url=url, callback=self.parse_moran_cert, dont_filter=True,
                                      errback=self.err_parse,
                                      method="POST", body=json.dumps(post_data), headers=self.moran_header,
                                      meta={"race_id": race_id, "user_id": user_id, "user_name": user_name,
                                            "race_name": race_name, "race_no": race_no,
                                            "score_data": score_data})
                    else:
                        result = self.result_item_assembler(response)
                        result['result_data'] = {"serialNumber": self.serialNumber, "webType": self.name_first,
                                                 "crawlerType": self.name_second, "data": str(score_data)}
                        yield result
            else:
                result = self.result_item_assembler(response)
                result['result_data'] = {"serialNumber": self.serialNumber, "webType": self.name_first,
                                         "crawlerType": self.name_second, "data": str({"msg": "未查到相关成绩信息"})}
                yield result
        except Exception:
            self.logger.info(f"{user_name}打码错误：{traceback.format_exc()}")

    def parse_moran_cert(self, response):
        race_id = response.meta.get("race_id", "")
        user_id = response.meta.get("user_id", "")
        user_name = response.meta.get("user_name", "")
        race_no = response.meta.get("race_no", "")
        score_data = response.meta.get("score_data", {})
        try:
            self.logger.info(f"获取{user_name}的证书信息")
            content = json.loads(response.text).get("text", "")
            content = json.loads(self.decryptDes(content))
            cert_url = content.get("certificateurl", "")
            if cert_url:
                yield Request(url=cert_url, callback=self.parse_pic, errback=self.err_parse, dont_filter=True,
                              meta={"race_id": race_id, "user_name": user_name, "user_id": user_id,
                                    "score_data": score_data, "race_no": race_no})
            else:
                result = self.result_item_assembler(response)
                result['result_data'] = {"serialNumber": self.serialNumber, "webType": self.name_first,
                                         "crawlerType": self.name_second, "data": str({"msg": "未查到相关成绩信息"})}
                yield result
        except Exception:
            self.logger.info(f"获取{user_name}的证书信息错误：{traceback.format_exc()}")

    def decrypt(self, data, aes_key="J4NULge4Tg7RcMaAusPhMBqEh5dUkPjY"):
        key = aes_key.encode('utf-8')
        if len(key) < 16:
            key = key.ljust(16, b'\0')
        elif len(key) < 24:
            key = key.ljust(24, b'\0')
        elif len(key) < 32:
            key = key.ljust(32, b'\0')
        else:
            key = key[:32]
        iv = key[:16]
        cipher = AES.new(key, AES.MODE_CBC, iv)
        encrypted_data = base64.b64decode(data)
        decrypted_data = cipher.decrypt(encrypted_data)
        unpadded_data = unpad(decrypted_data, AES.block_size)
        return unpadded_data.decode('utf-8')

    def decryptDes(self, data):
        key = self.des_key.encode('utf-8')
        if len(key) != 8:
            key = key.ljust(8, b'\0')[:8]
        ciphertext = bytes.fromhex(data)
        cipher = DES.new(key, DES.MODE_ECB)
        decrypted_data = cipher.decrypt(ciphertext)
        plaintext = unpad(decrypted_data, DES.block_size)
        return plaintext.decode('utf-8')

    def encryptDes(self, data):
        key = self.des_key.encode('utf-8')
        if len(key) != 8:
            key = key.ljust(8, b'\0')[:8]
        plaintext = data.encode('utf-8')
        cipher = DES.new(key, DES.MODE_ECB)
        padded_data = pad(plaintext, DES.block_size)
        encrypted_data = cipher.encrypt(padded_data)
        ciphertext_hex = binascii.hexlify(encrypted_data).decode('utf-8')
        return ciphertext_hex

    def err_parse(self, failure):
        self.logger.warning(f"请求失败：{failure.request.url},错误原因:{traceback.format_exc()}")

